1 /* 2 * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.management; 27 28 import static com.sun.jmx.defaults.JmxProperties.MISC_LOGGER; 29 import com.sun.jmx.mbeanserver.DescriptorCache; 30 import com.sun.jmx.mbeanserver.Introspector; 31 import com.sun.jmx.mbeanserver.MBeanSupport; 32 import com.sun.jmx.mbeanserver.MXBeanSupport; 33 import com.sun.jmx.mbeanserver.StandardMBeanSupport; 34 import com.sun.jmx.mbeanserver.Util; 35 36 import java.security.AccessController; 37 import java.security.PrivilegedAction; 38 import java.util.HashMap; 39 import java.util.Map; 40 import java.util.WeakHashMap; 41 import java.util.logging.Level; 42 import javax.management.openmbean.OpenMBeanAttributeInfo; 43 import javax.management.openmbean.OpenMBeanAttributeInfoSupport; 44 import javax.management.openmbean.OpenMBeanConstructorInfo; 45 import javax.management.openmbean.OpenMBeanConstructorInfoSupport; 46 import javax.management.openmbean.OpenMBeanOperationInfo; 47 import javax.management.openmbean.OpenMBeanOperationInfoSupport; 48 import javax.management.openmbean.OpenMBeanParameterInfo; 49 import javax.management.openmbean.OpenMBeanParameterInfoSupport; 50 51 /** 52 * <p>An MBean whose management interface is determined by reflection 53 * on a Java interface.</p> 54 * 55 * <p>This class brings more flexibility to the notion of Management 56 * Interface in the use of Standard MBeans. Straightforward use of 57 * the patterns for Standard MBeans described in the JMX Specification 58 * means that there is a fixed relationship between the implementation 59 * class of an MBean and its management interface (i.e., if the 60 * implementation class is Thing, the management interface must be 61 * ThingMBean). This class makes it possible to keep the convenience 62 * of specifying the management interface with a Java interface, 63 * without requiring that there be any naming relationship between the 64 * implementation and interface classes.</p> 65 * 66 * <p>By making a DynamicMBean out of an MBean, this class makes 67 * it possible to select any interface implemented by the MBean as its 68 * management interface, provided that it complies with JMX patterns 69 * (i.e., attributes defined by getter/setter etc...).</p> 70 * 71 * <p> This class also provides hooks that make it possible to supply 72 * custom descriptions and names for the {@link MBeanInfo} returned by 73 * the DynamicMBean interface.</p> 74 * 75 * <p>Using this class, an MBean can be created with any 76 * implementation class name <i>Impl</i> and with a management 77 * interface defined (as for current Standard MBeans) by any interface 78 * <i>Intf</i>, in one of two general ways:</p> 79 * 80 * <ul> 81 * 82 * <li>Using the public constructor 83 * {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean) 84 * StandardMBean(impl,interface)}: 85 * <pre> 86 * MBeanServer mbs; 87 * ... 88 * Impl impl = new Impl(...); 89 * StandardMBean mbean = new StandardMBean(impl, Intf.class, false); 90 * mbs.registerMBean(mbean, objectName); 91 * </pre></li> 92 * 93 * <li>Subclassing StandardMBean: 94 * <pre> 95 * public class Impl extends StandardMBean implements Intf { 96 * public Impl() { 97 * super(Intf.class, false); 98 * } 99 * // implement methods of Intf 100 * } 101 * 102 * [...] 103 * 104 * MBeanServer mbs; 105 * .... 106 * Impl impl = new Impl(); 107 * mbs.registerMBean(impl, objectName); 108 * </pre></li> 109 * 110 * </ul> 111 * 112 * <p>In either case, the class <i>Impl</i> must implement the 113 * interface <i>Intf</i>.</p> 114 * 115 * <p>Standard MBeans based on the naming relationship between 116 * implementation and interface classes are of course still 117 * available.</p> 118 * 119 * <p>This class may also be used to construct MXBeans. The usage 120 * is exactly the same as for Standard MBeans except that in the 121 * examples above, the {@code false} parameter to the constructor or 122 * {@code super(...)} invocation is instead {@code true}.</p> 123 * 124 * @since 1.5 125 */ 126 public class StandardMBean implements DynamicMBean, MBeanRegistration { 127 128 private final static DescriptorCache descriptors = 129 DescriptorCache.getInstance(JMX.proof); 130 131 /** 132 * The DynamicMBean that wraps the MXBean or Standard MBean implementation. 133 **/ 134 private volatile MBeanSupport<?> mbean; 135 136 /** 137 * The cached MBeanInfo. 138 **/ 139 private volatile MBeanInfo cachedMBeanInfo; 140 141 /** 142 * Make a DynamicMBean out of <var>implementation</var>, using the 143 * specified <var>mbeanInterface</var> class. 144 * @param implementation The implementation of this MBean. 145 * If <code>null</code>, and null implementation is allowed, 146 * then the implementation is assumed to be <var>this</var>. 147 * @param mbeanInterface The Management Interface exported by this 148 * MBean's implementation. If <code>null</code>, then this 149 * object will use standard JMX design pattern to determine 150 * the management interface associated with the given 151 * implementation. 152 * @param nullImplementationAllowed <code>true</code> if a null 153 * implementation is allowed. If null implementation is allowed, 154 * and a null implementation is passed, then the implementation 155 * is assumed to be <var>this</var>. 156 * @exception IllegalArgumentException if the given 157 * <var>implementation</var> is null, and null is not allowed. 158 **/ 159 private <T> void construct(T implementation, Class<T> mbeanInterface, 160 boolean nullImplementationAllowed, 161 boolean isMXBean) 162 throws NotCompliantMBeanException { 163 if (implementation == null) { 164 // Have to use (T)this rather than mbeanInterface.cast(this) 165 // because mbeanInterface might be null. 166 if (nullImplementationAllowed) 167 implementation = Util.<T>cast(this); 168 else throw new IllegalArgumentException("implementation is null"); 169 } 170 if (isMXBean) { 171 if (mbeanInterface == null) { 172 mbeanInterface = Util.cast(Introspector.getMXBeanInterface( 173 implementation.getClass())); 174 } 175 this.mbean = new MXBeanSupport(implementation, mbeanInterface); 176 } else { 177 if (mbeanInterface == null) { 178 mbeanInterface = Util.cast(Introspector.getStandardMBeanInterface( 179 implementation.getClass())); 180 } 181 this.mbean = 182 new StandardMBeanSupport(implementation, mbeanInterface); 183 } 184 } 185 186 /** 187 * <p>Make a DynamicMBean out of the object 188 * <var>implementation</var>, using the specified 189 * <var>mbeanInterface</var> class.</p> 190 * 191 * @param implementation The implementation of this MBean. 192 * @param mbeanInterface The Management Interface exported by this 193 * MBean's implementation. If <code>null</code>, then this 194 * object will use standard JMX design pattern to determine 195 * the management interface associated with the given 196 * implementation. 197 * @param <T> Allows the compiler to check 198 * that {@code implementation} does indeed implement the class 199 * described by {@code mbeanInterface}. The compiler can only 200 * check this if {@code mbeanInterface} is a class literal such 201 * as {@code MyMBean.class}. 202 * 203 * @exception IllegalArgumentException if the given 204 * <var>implementation</var> is null. 205 * @exception NotCompliantMBeanException if the <var>mbeanInterface</var> 206 * does not follow JMX design patterns for Management Interfaces, or 207 * if the given <var>implementation</var> does not implement the 208 * specified interface. 209 **/ 210 public <T> StandardMBean(T implementation, Class<T> mbeanInterface) 211 throws NotCompliantMBeanException { 212 construct(implementation, mbeanInterface, false, false); 213 } 214 215 /** 216 * <p>Make a DynamicMBean out of <var>this</var>, using the specified 217 * <var>mbeanInterface</var> class.</p> 218 * 219 * <p>Calls {@link #StandardMBean(java.lang.Object, java.lang.Class) 220 * this(this,mbeanInterface)}. 221 * This constructor is reserved to subclasses.</p> 222 * 223 * @param mbeanInterface The Management Interface exported by this 224 * MBean. 225 * 226 * @exception NotCompliantMBeanException if the <var>mbeanInterface</var> 227 * does not follow JMX design patterns for Management Interfaces, or 228 * if <var>this</var> does not implement the specified interface. 229 **/ 230 protected StandardMBean(Class<?> mbeanInterface) 231 throws NotCompliantMBeanException { 232 construct(null, mbeanInterface, true, false); 233 } 234 235 /** 236 * <p>Make a DynamicMBean out of the object 237 * <var>implementation</var>, using the specified 238 * <var>mbeanInterface</var> class, and choosing whether the 239 * resultant MBean is an MXBean. This constructor can be used 240 * to make either Standard MBeans or MXBeans. Unlike the 241 * constructor {@link #StandardMBean(Object, Class)}, it 242 * does not throw NotCompliantMBeanException.</p> 243 * 244 * @param implementation The implementation of this MBean. 245 * @param mbeanInterface The Management Interface exported by this 246 * MBean's implementation. If <code>null</code>, then this 247 * object will use standard JMX design pattern to determine 248 * the management interface associated with the given 249 * implementation. 250 * @param isMXBean If true, the {@code mbeanInterface} parameter 251 * names an MXBean interface and the resultant MBean is an MXBean. 252 * @param <T> Allows the compiler to check 253 * that {@code implementation} does indeed implement the class 254 * described by {@code mbeanInterface}. The compiler can only 255 * check this if {@code mbeanInterface} is a class literal such 256 * as {@code MyMBean.class}. 257 * 258 * @exception IllegalArgumentException if the given 259 * <var>implementation</var> is null, or if the <var>mbeanInterface</var> 260 * does not follow JMX design patterns for Management Interfaces, or 261 * if the given <var>implementation</var> does not implement the 262 * specified interface. 263 * 264 * @since 1.6 265 **/ 266 public <T> StandardMBean(T implementation, Class<T> mbeanInterface, 267 boolean isMXBean) { 268 try { 269 construct(implementation, mbeanInterface, false, isMXBean); 270 } catch (NotCompliantMBeanException e) { 271 throw new IllegalArgumentException(e); 272 } 273 } 274 275 /** 276 * <p>Make a DynamicMBean out of <var>this</var>, using the specified 277 * <var>mbeanInterface</var> class, and choosing whether the resulting 278 * MBean is an MXBean. This constructor can be used 279 * to make either Standard MBeans or MXBeans. Unlike the 280 * constructor {@link #StandardMBean(Object, Class)}, it 281 * does not throw NotCompliantMBeanException.</p> 282 * 283 * <p>Calls {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean) 284 * this(this, mbeanInterface, isMXBean)}. 285 * This constructor is reserved to subclasses.</p> 286 * 287 * @param mbeanInterface The Management Interface exported by this 288 * MBean. 289 * @param isMXBean If true, the {@code mbeanInterface} parameter 290 * names an MXBean interface and the resultant MBean is an MXBean. 291 * 292 * @exception IllegalArgumentException if the <var>mbeanInterface</var> 293 * does not follow JMX design patterns for Management Interfaces, or 294 * if <var>this</var> does not implement the specified interface. 295 * 296 * @since 1.6 297 **/ 298 protected StandardMBean(Class<?> mbeanInterface, boolean isMXBean) { 299 try { 300 construct(null, mbeanInterface, true, isMXBean); 301 } catch (NotCompliantMBeanException e) { 302 throw new IllegalArgumentException(e); 303 } 304 } 305 306 /** 307 * <p>Replace the implementation object wrapped in this object.</p> 308 * 309 * @param implementation The new implementation of this Standard MBean 310 * (or MXBean). The <code>implementation</code> object must implement 311 * the Standard MBean (or MXBean) interface that was supplied when this 312 * <code>StandardMBean</code> was constructed. 313 * 314 * @exception IllegalArgumentException if the given 315 * <var>implementation</var> is null. 316 * 317 * @exception NotCompliantMBeanException if the given 318 * <var>implementation</var> does not implement the 319 * Standard MBean (or MXBean) interface that was 320 * supplied at construction. 321 * 322 * @see #getImplementation 323 **/ 324 public void setImplementation(Object implementation) 325 throws NotCompliantMBeanException { 326 327 if (implementation == null) 328 throw new IllegalArgumentException("implementation is null"); 329 330 if (isMXBean()) { 331 this.mbean = new MXBeanSupport(implementation, 332 Util.<Class<Object>>cast(getMBeanInterface())); 333 } else { 334 this.mbean = new StandardMBeanSupport(implementation, 335 Util.<Class<Object>>cast(getMBeanInterface())); 336 } 337 } 338 339 /** 340 * Get the implementation of this Standard MBean (or MXBean). 341 * @return The implementation of this Standard MBean (or MXBean). 342 * 343 * @see #setImplementation 344 **/ 345 public Object getImplementation() { 346 return mbean.getResource(); 347 } 348 349 /** 350 * Get the Management Interface of this Standard MBean (or MXBean). 351 * @return The management interface of this Standard MBean (or MXBean). 352 **/ 353 public final Class<?> getMBeanInterface() { 354 return mbean.getMBeanInterface(); 355 } 356 357 /** 358 * Get the class of the implementation of this Standard MBean (or MXBean). 359 * @return The class of the implementation of this Standard MBean (or MXBean). 360 **/ 361 public Class<?> getImplementationClass() { 362 return mbean.getResource().getClass(); 363 } 364 365 // ------------------------------------------------------------------ 366 // From the DynamicMBean interface. 367 // ------------------------------------------------------------------ 368 public Object getAttribute(String attribute) 369 throws AttributeNotFoundException, 370 MBeanException, 371 ReflectionException { 372 return mbean.getAttribute(attribute); 373 } 374 375 // ------------------------------------------------------------------ 376 // From the DynamicMBean interface. 377 // ------------------------------------------------------------------ 378 public void setAttribute(Attribute attribute) 379 throws AttributeNotFoundException, 380 InvalidAttributeValueException, 381 MBeanException, 382 ReflectionException { 383 mbean.setAttribute(attribute); 384 } 385 386 // ------------------------------------------------------------------ 387 // From the DynamicMBean interface. 388 // ------------------------------------------------------------------ 389 public AttributeList getAttributes(String[] attributes) { 390 return mbean.getAttributes(attributes); 391 } 392 393 // ------------------------------------------------------------------ 394 // From the DynamicMBean interface. 395 // ------------------------------------------------------------------ 396 public AttributeList setAttributes(AttributeList attributes) { 397 return mbean.setAttributes(attributes); 398 } 399 400 // ------------------------------------------------------------------ 401 // From the DynamicMBean interface. 402 // ------------------------------------------------------------------ 403 public Object invoke(String actionName, Object params[], String signature[]) 404 throws MBeanException, ReflectionException { 405 return mbean.invoke(actionName, params, signature); 406 } 407 408 /** 409 * Get the {@link MBeanInfo} for this MBean. 410 * <p> 411 * This method implements 412 * {@link javax.management.DynamicMBean#getMBeanInfo() 413 * DynamicMBean.getMBeanInfo()}. 414 * <p> 415 * This method first calls {@link #getCachedMBeanInfo()} in order to 416 * retrieve the cached MBeanInfo for this MBean, if any. If the 417 * MBeanInfo returned by {@link #getCachedMBeanInfo()} is not null, 418 * then it is returned.<br> 419 * Otherwise, this method builds a default MBeanInfo for this MBean, 420 * using the Management Interface specified for this MBean. 421 * <p> 422 * While building the MBeanInfo, this method calls the customization 423 * hooks that make it possible for subclasses to supply their custom 424 * descriptions, parameter names, etc...<br> 425 * Finally, it calls {@link #cacheMBeanInfo(javax.management.MBeanInfo) 426 * cacheMBeanInfo()} in order to cache the new MBeanInfo. 427 * @return The cached MBeanInfo for that MBean, if not null, or a 428 * newly built MBeanInfo if none was cached. 429 **/ 430 public MBeanInfo getMBeanInfo() { 431 try { 432 final MBeanInfo cached = getCachedMBeanInfo(); 433 if (cached != null) return cached; 434 } catch (RuntimeException x) { 435 if (MISC_LOGGER.isLoggable(Level.FINEST)) { 436 MISC_LOGGER.logp(Level.FINEST, 437 MBeanServerFactory.class.getName(), "getMBeanInfo", 438 "Failed to get cached MBeanInfo", x); 439 } 440 } 441 442 if (MISC_LOGGER.isLoggable(Level.FINER)) { 443 MISC_LOGGER.logp(Level.FINER, 444 MBeanServerFactory.class.getName(), "getMBeanInfo", 445 "Building MBeanInfo for " + 446 getImplementationClass().getName()); 447 } 448 449 MBeanSupport<?> msupport = mbean; 450 final MBeanInfo bi = msupport.getMBeanInfo(); 451 final Object impl = msupport.getResource(); 452 453 final boolean immutableInfo = immutableInfo(this.getClass()); 454 455 final String cname = getClassName(bi); 456 final String text = getDescription(bi); 457 final MBeanConstructorInfo[] ctors = getConstructors(bi,impl); 458 final MBeanAttributeInfo[] attrs = getAttributes(bi); 459 final MBeanOperationInfo[] ops = getOperations(bi); 460 final MBeanNotificationInfo[] ntfs = getNotifications(bi); 461 final Descriptor desc = getDescriptor(bi, immutableInfo); 462 463 final MBeanInfo nmbi = new MBeanInfo( 464 cname, text, attrs, ctors, ops, ntfs, desc); 465 try { 466 cacheMBeanInfo(nmbi); 467 } catch (RuntimeException x) { 468 if (MISC_LOGGER.isLoggable(Level.FINEST)) { 469 MISC_LOGGER.logp(Level.FINEST, 470 MBeanServerFactory.class.getName(), "getMBeanInfo", 471 "Failed to cache MBeanInfo", x); 472 } 473 } 474 475 return nmbi; 476 } 477 478 /** 479 * Customization hook: 480 * Get the className that will be used in the MBeanInfo returned by 481 * this MBean. 482 * <br> 483 * Subclasses may redefine this method in order to supply their 484 * custom class name. The default implementation returns 485 * {@link MBeanInfo#getClassName() info.getClassName()}. 486 * @param info The default MBeanInfo derived by reflection. 487 * @return the class name for the new MBeanInfo. 488 **/ 489 protected String getClassName(MBeanInfo info) { 490 if (info == null) return getImplementationClass().getName(); 491 return info.getClassName(); 492 } 493 494 /** 495 * Customization hook: 496 * Get the description that will be used in the MBeanInfo returned by 497 * this MBean. 498 * <br> 499 * Subclasses may redefine this method in order to supply their 500 * custom MBean description. The default implementation returns 501 * {@link MBeanInfo#getDescription() info.getDescription()}. 502 * @param info The default MBeanInfo derived by reflection. 503 * @return the description for the new MBeanInfo. 504 **/ 505 protected String getDescription(MBeanInfo info) { 506 if (info == null) return null; 507 return info.getDescription(); 508 } 509 510 /** 511 * <p>Customization hook: 512 * Get the description that will be used in the MBeanFeatureInfo 513 * returned by this MBean.</p> 514 * 515 * <p>Subclasses may redefine this method in order to supply 516 * their custom description. The default implementation returns 517 * {@link MBeanFeatureInfo#getDescription() 518 * info.getDescription()}.</p> 519 * 520 * <p>This method is called by 521 * {@link #getDescription(MBeanAttributeInfo)}, 522 * {@link #getDescription(MBeanOperationInfo)}, 523 * {@link #getDescription(MBeanConstructorInfo)}.</p> 524 * 525 * @param info The default MBeanFeatureInfo derived by reflection. 526 * @return the description for the given MBeanFeatureInfo. 527 **/ 528 protected String getDescription(MBeanFeatureInfo info) { 529 if (info == null) return null; 530 return info.getDescription(); 531 } 532 533 /** 534 * Customization hook: 535 * Get the description that will be used in the MBeanAttributeInfo 536 * returned by this MBean. 537 * 538 * <p>Subclasses may redefine this method in order to supply their 539 * custom description. The default implementation returns {@link 540 * #getDescription(MBeanFeatureInfo) 541 * getDescription((MBeanFeatureInfo) info)}. 542 * @param info The default MBeanAttributeInfo derived by reflection. 543 * @return the description for the given MBeanAttributeInfo. 544 **/ 545 protected String getDescription(MBeanAttributeInfo info) { 546 return getDescription((MBeanFeatureInfo)info); 547 } 548 549 /** 550 * Customization hook: 551 * Get the description that will be used in the MBeanConstructorInfo 552 * returned by this MBean. 553 * <br> 554 * Subclasses may redefine this method in order to supply their 555 * custom description. 556 * The default implementation returns {@link 557 * #getDescription(MBeanFeatureInfo) 558 * getDescription((MBeanFeatureInfo) info)}. 559 * @param info The default MBeanConstructorInfo derived by reflection. 560 * @return the description for the given MBeanConstructorInfo. 561 **/ 562 protected String getDescription(MBeanConstructorInfo info) { 563 return getDescription((MBeanFeatureInfo)info); 564 } 565 566 /** 567 * Customization hook: 568 * Get the description that will be used for the <var>sequence</var> 569 * MBeanParameterInfo of the MBeanConstructorInfo returned by this MBean. 570 * <br> 571 * Subclasses may redefine this method in order to supply their 572 * custom description. The default implementation returns 573 * {@link MBeanParameterInfo#getDescription() param.getDescription()}. 574 * 575 * @param ctor The default MBeanConstructorInfo derived by reflection. 576 * @param param The default MBeanParameterInfo derived by reflection. 577 * @param sequence The sequence number of the parameter considered 578 * ("0" for the first parameter, "1" for the second parameter, 579 * etc...). 580 * @return the description for the given MBeanParameterInfo. 581 **/ 582 protected String getDescription(MBeanConstructorInfo ctor, 583 MBeanParameterInfo param, 584 int sequence) { 585 if (param == null) return null; 586 return param.getDescription(); 587 } 588 589 /** 590 * Customization hook: 591 * Get the name that will be used for the <var>sequence</var> 592 * MBeanParameterInfo of the MBeanConstructorInfo returned by this MBean. 593 * <br> 594 * Subclasses may redefine this method in order to supply their 595 * custom parameter name. The default implementation returns 596 * {@link MBeanParameterInfo#getName() param.getName()}. 597 * 598 * @param ctor The default MBeanConstructorInfo derived by reflection. 599 * @param param The default MBeanParameterInfo derived by reflection. 600 * @param sequence The sequence number of the parameter considered 601 * ("0" for the first parameter, "1" for the second parameter, 602 * etc...). 603 * @return the name for the given MBeanParameterInfo. 604 **/ 605 protected String getParameterName(MBeanConstructorInfo ctor, 606 MBeanParameterInfo param, 607 int sequence) { 608 if (param == null) return null; 609 return param.getName(); 610 } 611 612 /** 613 * Customization hook: 614 * Get the description that will be used in the MBeanOperationInfo 615 * returned by this MBean. 616 * <br> 617 * Subclasses may redefine this method in order to supply their 618 * custom description. The default implementation returns 619 * {@link #getDescription(MBeanFeatureInfo) 620 * getDescription((MBeanFeatureInfo) info)}. 621 * @param info The default MBeanOperationInfo derived by reflection. 622 * @return the description for the given MBeanOperationInfo. 623 **/ 624 protected String getDescription(MBeanOperationInfo info) { 625 return getDescription((MBeanFeatureInfo)info); 626 } 627 628 /** 629 * Customization hook: 630 * Get the <var>impact</var> flag of the operation that will be used in 631 * the MBeanOperationInfo returned by this MBean. 632 * <br> 633 * Subclasses may redefine this method in order to supply their 634 * custom impact flag. The default implementation returns 635 * {@link MBeanOperationInfo#getImpact() info.getImpact()}. 636 * @param info The default MBeanOperationInfo derived by reflection. 637 * @return the impact flag for the given MBeanOperationInfo. 638 **/ 639 protected int getImpact(MBeanOperationInfo info) { 640 if (info == null) return MBeanOperationInfo.UNKNOWN; 641 return info.getImpact(); 642 } 643 644 /** 645 * Customization hook: 646 * Get the name that will be used for the <var>sequence</var> 647 * MBeanParameterInfo of the MBeanOperationInfo returned by this MBean. 648 * <br> 649 * Subclasses may redefine this method in order to supply their 650 * custom parameter name. The default implementation returns 651 * {@link MBeanParameterInfo#getName() param.getName()}. 652 * 653 * @param op The default MBeanOperationInfo derived by reflection. 654 * @param param The default MBeanParameterInfo derived by reflection. 655 * @param sequence The sequence number of the parameter considered 656 * ("0" for the first parameter, "1" for the second parameter, 657 * etc...). 658 * @return the name to use for the given MBeanParameterInfo. 659 **/ 660 protected String getParameterName(MBeanOperationInfo op, 661 MBeanParameterInfo param, 662 int sequence) { 663 if (param == null) return null; 664 return param.getName(); 665 } 666 667 /** 668 * Customization hook: 669 * Get the description that will be used for the <var>sequence</var> 670 * MBeanParameterInfo of the MBeanOperationInfo returned by this MBean. 671 * <br> 672 * Subclasses may redefine this method in order to supply their 673 * custom description. The default implementation returns 674 * {@link MBeanParameterInfo#getDescription() param.getDescription()}. 675 * 676 * @param op The default MBeanOperationInfo derived by reflection. 677 * @param param The default MBeanParameterInfo derived by reflection. 678 * @param sequence The sequence number of the parameter considered 679 * ("0" for the first parameter, "1" for the second parameter, 680 * etc...). 681 * @return the description for the given MBeanParameterInfo. 682 **/ 683 protected String getDescription(MBeanOperationInfo op, 684 MBeanParameterInfo param, 685 int sequence) { 686 if (param == null) return null; 687 return param.getDescription(); 688 } 689 690 /** 691 * Customization hook: 692 * Get the MBeanConstructorInfo[] that will be used in the MBeanInfo 693 * returned by this MBean. 694 * <br> 695 * By default, this method returns <code>null</code> if the wrapped 696 * implementation is not <var>this</var>. Indeed, if the wrapped 697 * implementation is not this object itself, it will not be possible 698 * to recreate a wrapped implementation by calling the implementation 699 * constructors through <code>MBeanServer.createMBean(...)</code>.<br> 700 * Otherwise, if the wrapped implementation is <var>this</var>, 701 * <var>ctors</var> is returned. 702 * <br> 703 * Subclasses may redefine this method in order to modify this 704 * behavior, if needed. 705 * @param ctors The default MBeanConstructorInfo[] derived by reflection. 706 * @param impl The wrapped implementation. If <code>null</code> is 707 * passed, the wrapped implementation is ignored and 708 * <var>ctors</var> is returned. 709 * @return the MBeanConstructorInfo[] for the new MBeanInfo. 710 **/ 711 protected MBeanConstructorInfo[] 712 getConstructors(MBeanConstructorInfo[] ctors, Object impl) { 713 if (ctors == null) return null; 714 if (impl != null && impl != this) return null; 715 return ctors; 716 } 717 718 /** 719 * Customization hook: 720 * Get the MBeanNotificationInfo[] that will be used in the MBeanInfo 721 * returned by this MBean. 722 * <br> 723 * Subclasses may redefine this method in order to supply their 724 * custom notifications. 725 * @param info The default MBeanInfo derived by reflection. 726 * @return the MBeanNotificationInfo[] for the new MBeanInfo. 727 **/ 728 MBeanNotificationInfo[] getNotifications(MBeanInfo info) { 729 return null; 730 } 731 732 /** 733 * <p>Get the Descriptor that will be used in the MBeanInfo 734 * returned by this MBean.</p> 735 * 736 * <p>Subclasses may redefine this method in order to supply 737 * their custom descriptor.</p> 738 * 739 * <p>The default implementation of this method returns a Descriptor 740 * that contains at least the field {@code interfaceClassName}, with 741 * value {@link #getMBeanInterface()}.getName(). It may also contain 742 * the field {@code immutableInfo}, with a value that is the string 743 * {@code "true"} if the implementation can determine that the 744 * {@code MBeanInfo} returned by {@link #getMBeanInfo()} will always 745 * be the same. It may contain other fields: fields defined by the 746 * JMX specification must have appropriate values, and other fields 747 * must follow the conventions for non-standard field names.</p> 748 * 749 * @param info The default MBeanInfo derived by reflection. 750 * @return the Descriptor for the new MBeanInfo. 751 */ 752 Descriptor getDescriptor(MBeanInfo info, boolean immutableInfo) { 753 ImmutableDescriptor desc; 754 if (info == null || 755 info.getDescriptor() == null || 756 info.getDescriptor().getFieldNames().length == 0) { 757 final String interfaceClassNameS = 758 "interfaceClassName=" + getMBeanInterface().getName(); 759 final String immutableInfoS = 760 "immutableInfo=" + immutableInfo; 761 desc = new ImmutableDescriptor(interfaceClassNameS, immutableInfoS); 762 desc = descriptors.get(desc); 763 } else { 764 Descriptor d = info.getDescriptor(); 765 Map<String,Object> fields = new HashMap<String,Object>(); 766 for (String fieldName : d.getFieldNames()) { 767 if (fieldName.equals("immutableInfo")) { 768 // Replace immutableInfo as the underlying MBean/MXBean 769 // could already implement NotificationBroadcaster and 770 // return immutableInfo=true in its MBeanInfo. 771 fields.put(fieldName, Boolean.toString(immutableInfo)); 772 } else { 773 fields.put(fieldName, d.getFieldValue(fieldName)); 774 } 775 } 776 desc = new ImmutableDescriptor(fields); 777 } 778 return desc; 779 } 780 781 /** 782 * Customization hook: 783 * Return the MBeanInfo cached for this object. 784 * 785 * <p>Subclasses may redefine this method in order to implement their 786 * own caching policy. The default implementation stores one 787 * {@link MBeanInfo} object per instance. 788 * 789 * @return The cached MBeanInfo, or null if no MBeanInfo is cached. 790 * 791 * @see #cacheMBeanInfo(MBeanInfo) 792 **/ 793 protected MBeanInfo getCachedMBeanInfo() { 794 return cachedMBeanInfo; 795 } 796 797 /** 798 * Customization hook: 799 * cache the MBeanInfo built for this object. 800 * 801 * <p>Subclasses may redefine this method in order to implement 802 * their own caching policy. The default implementation stores 803 * <code>info</code> in this instance. A subclass can define 804 * other policies, such as not saving <code>info</code> (so it is 805 * reconstructed every time {@link #getMBeanInfo()} is called) or 806 * sharing a unique {@link MBeanInfo} object when several 807 * <code>StandardMBean</code> instances have equal {@link 808 * MBeanInfo} values. 809 * 810 * @param info the new <code>MBeanInfo</code> to cache. Any 811 * previously cached value is discarded. This parameter may be 812 * null, in which case there is no new cached value. 813 **/ 814 protected void cacheMBeanInfo(MBeanInfo info) { 815 cachedMBeanInfo = info; 816 } 817 818 private boolean isMXBean() { 819 return mbean.isMXBean(); 820 } 821 822 private static <T> boolean identicalArrays(T[] a, T[] b) { 823 if (a == b) 824 return true; 825 if (a == null || b == null || a.length != b.length) 826 return false; 827 for (int i = 0; i < a.length; i++) { 828 if (a[i] != b[i]) 829 return false; 830 } 831 return true; 832 } 833 834 private static <T> boolean equal(T a, T b) { 835 if (a == b) 836 return true; 837 if (a == null || b == null) 838 return false; 839 return a.equals(b); 840 } 841 842 private static MBeanParameterInfo 843 customize(MBeanParameterInfo pi, 844 String name, 845 String description) { 846 if (equal(name, pi.getName()) && 847 equal(description, pi.getDescription())) 848 return pi; 849 else if (pi instanceof OpenMBeanParameterInfo) { 850 OpenMBeanParameterInfo opi = (OpenMBeanParameterInfo) pi; 851 return new OpenMBeanParameterInfoSupport(name, 852 description, 853 opi.getOpenType(), 854 pi.getDescriptor()); 855 } else { 856 return new MBeanParameterInfo(name, 857 pi.getType(), 858 description, 859 pi.getDescriptor()); 860 } 861 } 862 863 private static MBeanConstructorInfo 864 customize(MBeanConstructorInfo ci, 865 String description, 866 MBeanParameterInfo[] signature) { 867 if (equal(description, ci.getDescription()) && 868 identicalArrays(signature, ci.getSignature())) 869 return ci; 870 if (ci instanceof OpenMBeanConstructorInfo) { 871 OpenMBeanParameterInfo[] oparams = 872 paramsToOpenParams(signature); 873 return new OpenMBeanConstructorInfoSupport(ci.getName(), 874 description, 875 oparams, 876 ci.getDescriptor()); 877 } else { 878 return new MBeanConstructorInfo(ci.getName(), 879 description, 880 signature, 881 ci.getDescriptor()); 882 } 883 } 884 885 private static MBeanOperationInfo 886 customize(MBeanOperationInfo oi, 887 String description, 888 MBeanParameterInfo[] signature, 889 int impact) { 890 if (equal(description, oi.getDescription()) && 891 identicalArrays(signature, oi.getSignature()) && 892 impact == oi.getImpact()) 893 return oi; 894 if (oi instanceof OpenMBeanOperationInfo) { 895 OpenMBeanOperationInfo ooi = (OpenMBeanOperationInfo) oi; 896 OpenMBeanParameterInfo[] oparams = 897 paramsToOpenParams(signature); 898 return new OpenMBeanOperationInfoSupport(oi.getName(), 899 description, 900 oparams, 901 ooi.getReturnOpenType(), 902 impact, 903 oi.getDescriptor()); 904 } else { 905 return new MBeanOperationInfo(oi.getName(), 906 description, 907 signature, 908 oi.getReturnType(), 909 impact, 910 oi.getDescriptor()); 911 } 912 } 913 914 private static MBeanAttributeInfo 915 customize(MBeanAttributeInfo ai, 916 String description) { 917 if (equal(description, ai.getDescription())) 918 return ai; 919 if (ai instanceof OpenMBeanAttributeInfo) { 920 OpenMBeanAttributeInfo oai = (OpenMBeanAttributeInfo) ai; 921 return new OpenMBeanAttributeInfoSupport(ai.getName(), 922 description, 923 oai.getOpenType(), 924 ai.isReadable(), 925 ai.isWritable(), 926 ai.isIs(), 927 ai.getDescriptor()); 928 } else { 929 return new MBeanAttributeInfo(ai.getName(), 930 ai.getType(), 931 description, 932 ai.isReadable(), 933 ai.isWritable(), 934 ai.isIs(), 935 ai.getDescriptor()); 936 } 937 } 938 939 private static OpenMBeanParameterInfo[] 940 paramsToOpenParams(MBeanParameterInfo[] params) { 941 if (params instanceof OpenMBeanParameterInfo[]) 942 return (OpenMBeanParameterInfo[]) params; 943 OpenMBeanParameterInfo[] oparams = 944 new OpenMBeanParameterInfoSupport[params.length]; 945 System.arraycopy(params, 0, oparams, 0, params.length); 946 return oparams; 947 } 948 949 // ------------------------------------------------------------------ 950 // Build the custom MBeanConstructorInfo[] 951 // ------------------------------------------------------------------ 952 private MBeanConstructorInfo[] 953 getConstructors(MBeanInfo info, Object impl) { 954 final MBeanConstructorInfo[] ctors = 955 getConstructors(info.getConstructors(), impl); 956 if (ctors == null) 957 return null; 958 final int ctorlen = ctors.length; 959 final MBeanConstructorInfo[] nctors = new MBeanConstructorInfo[ctorlen]; 960 for (int i=0; i<ctorlen; i++) { 961 final MBeanConstructorInfo c = ctors[i]; 962 final MBeanParameterInfo[] params = c.getSignature(); 963 final MBeanParameterInfo[] nps; 964 if (params != null) { 965 final int plen = params.length; 966 nps = new MBeanParameterInfo[plen]; 967 for (int ii=0;ii<plen;ii++) { 968 MBeanParameterInfo p = params[ii]; 969 nps[ii] = customize(p, 970 getParameterName(c,p,ii), 971 getDescription(c,p,ii)); 972 } 973 } else { 974 nps = null; 975 } 976 nctors[i] = 977 customize(c, getDescription(c), nps); 978 } 979 return nctors; 980 } 981 982 // ------------------------------------------------------------------ 983 // Build the custom MBeanOperationInfo[] 984 // ------------------------------------------------------------------ 985 private MBeanOperationInfo[] getOperations(MBeanInfo info) { 986 final MBeanOperationInfo[] ops = info.getOperations(); 987 if (ops == null) 988 return null; 989 final int oplen = ops.length; 990 final MBeanOperationInfo[] nops = new MBeanOperationInfo[oplen]; 991 for (int i=0; i<oplen; i++) { 992 final MBeanOperationInfo o = ops[i]; 993 final MBeanParameterInfo[] params = o.getSignature(); 994 final MBeanParameterInfo[] nps; 995 if (params != null) { 996 final int plen = params.length; 997 nps = new MBeanParameterInfo[plen]; 998 for (int ii=0;ii<plen;ii++) { 999 MBeanParameterInfo p = params[ii]; 1000 nps[ii] = customize(p, 1001 getParameterName(o,p,ii), 1002 getDescription(o,p,ii)); 1003 } 1004 } else { 1005 nps = null; 1006 } 1007 nops[i] = customize(o, getDescription(o), nps, getImpact(o)); 1008 } 1009 return nops; 1010 } 1011 1012 // ------------------------------------------------------------------ 1013 // Build the custom MBeanAttributeInfo[] 1014 // ------------------------------------------------------------------ 1015 private MBeanAttributeInfo[] getAttributes(MBeanInfo info) { 1016 final MBeanAttributeInfo[] atts = info.getAttributes(); 1017 if (atts == null) 1018 return null; // should not happen 1019 final MBeanAttributeInfo[] natts; 1020 final int attlen = atts.length; 1021 natts = new MBeanAttributeInfo[attlen]; 1022 for (int i=0; i<attlen; i++) { 1023 final MBeanAttributeInfo a = atts[i]; 1024 natts[i] = customize(a, getDescription(a)); 1025 } 1026 return natts; 1027 } 1028 1029 /** 1030 * <p>Allows the MBean to perform any operations it needs before 1031 * being registered in the MBean server. If the name of the MBean 1032 * is not specified, the MBean can provide a name for its 1033 * registration. If any exception is raised, the MBean will not be 1034 * registered in the MBean server.</p> 1035 * 1036 * <p>The default implementation of this method returns the {@code name} 1037 * parameter. It does nothing else for 1038 * Standard MBeans. For MXBeans, it records the {@code MBeanServer} 1039 * and {@code ObjectName} parameters so they can be used to translate 1040 * inter-MXBean references.</p> 1041 * 1042 * <p>It is good practice for a subclass that overrides this method 1043 * to call the overridden method via {@code super.preRegister(...)}. 1044 * This is necessary if this object is an MXBean that is referenced 1045 * by attributes or operations in other MXBeans.</p> 1046 * 1047 * @param server The MBean server in which the MBean will be registered. 1048 * 1049 * @param name The object name of the MBean. This name is null if 1050 * the name parameter to one of the <code>createMBean</code> or 1051 * <code>registerMBean</code> methods in the {@link MBeanServer} 1052 * interface is null. In that case, this method must return a 1053 * non-null ObjectName for the new MBean. 1054 * 1055 * @return The name under which the MBean is to be registered. 1056 * This value must not be null. If the <code>name</code> 1057 * parameter is not null, it will usually but not necessarily be 1058 * the returned value. 1059 * 1060 * @throws IllegalArgumentException if this is an MXBean and 1061 * {@code name} is null. 1062 * 1063 * @throws InstanceAlreadyExistsException if this is an MXBean and 1064 * it has already been registered under another name (in this 1065 * MBean Server or another). 1066 * 1067 * @throws Exception no other checked exceptions are thrown by 1068 * this method but {@code Exception} is declared so that subclasses 1069 * can override the method and throw their own exceptions. 1070 * 1071 * @since 1.6 1072 */ 1073 public ObjectName preRegister(MBeanServer server, ObjectName name) 1074 throws Exception { 1075 mbean.register(server, name); 1076 return name; 1077 } 1078 1079 /** 1080 * <p>Allows the MBean to perform any operations needed after having been 1081 * registered in the MBean server or after the registration has failed.</p> 1082 * 1083 * <p>The default implementation of this method does nothing for 1084 * Standard MBeans. For MXBeans, it undoes any work done by 1085 * {@link #preRegister preRegister} if registration fails.</p> 1086 * 1087 * <p>It is good practice for a subclass that overrides this method 1088 * to call the overridden method via {@code super.postRegister(...)}. 1089 * This is necessary if this object is an MXBean that is referenced 1090 * by attributes or operations in other MXBeans.</p> 1091 * 1092 * @param registrationDone Indicates whether or not the MBean has 1093 * been successfully registered in the MBean server. The value 1094 * false means that the registration phase has failed. 1095 * 1096 * @since 1.6 1097 */ 1098 public void postRegister(Boolean registrationDone) { 1099 if (!registrationDone) 1100 mbean.unregister(); 1101 } 1102 1103 /** 1104 * <p>Allows the MBean to perform any operations it needs before 1105 * being unregistered by the MBean server.</p> 1106 * 1107 * <p>The default implementation of this method does nothing.</p> 1108 * 1109 * <p>It is good practice for a subclass that overrides this method 1110 * to call the overridden method via {@code super.preDeregister(...)}.</p> 1111 * 1112 * @throws Exception no checked exceptions are throw by this method 1113 * but {@code Exception} is declared so that subclasses can override 1114 * this method and throw their own exceptions. 1115 * 1116 * @since 1.6 1117 */ 1118 public void preDeregister() throws Exception { 1119 } 1120 1121 /** 1122 * <p>Allows the MBean to perform any operations needed after having been 1123 * unregistered in the MBean server.</p> 1124 * 1125 * <p>The default implementation of this method does nothing for 1126 * Standard MBeans. For MXBeans, it removes any information that 1127 * was recorded by the {@link #preRegister preRegister} method.</p> 1128 * 1129 * <p>It is good practice for a subclass that overrides this method 1130 * to call the overridden method via {@code super.postRegister(...)}. 1131 * This is necessary if this object is an MXBean that is referenced 1132 * by attributes or operations in other MXBeans.</p> 1133 * 1134 * @since 1.6 1135 */ 1136 public void postDeregister() { 1137 mbean.unregister(); 1138 } 1139 1140 // 1141 // MBeanInfo immutability 1142 // 1143 1144 /** 1145 * Cached results of previous calls to immutableInfo. This is 1146 * a WeakHashMap so that we don't prevent a class from being 1147 * garbage collected just because we know whether its MBeanInfo 1148 * is immutable. 1149 */ 1150 private static final Map<Class<?>, Boolean> mbeanInfoSafeMap = 1151 new WeakHashMap<Class<?>, Boolean>(); 1152 1153 /** 1154 * Return true if {@code subclass} is known to preserve the immutability 1155 * of the {@code MBeanInfo}. The {@code subclass} is considered to have 1156 * an immutable {@code MBeanInfo} if it does not override any of the 1157 * getMBeanInfo, getCachedMBeanInfo, cacheMBeanInfo and getNotificationInfo 1158 * methods. 1159 */ 1160 static boolean immutableInfo(Class<? extends StandardMBean> subclass) { 1161 if (subclass == StandardMBean.class || 1162 subclass == StandardEmitterMBean.class) 1163 return true; 1164 synchronized (mbeanInfoSafeMap) { 1165 Boolean safe = mbeanInfoSafeMap.get(subclass); 1166 if (safe == null) { 1167 try { 1168 MBeanInfoSafeAction action = 1169 new MBeanInfoSafeAction(subclass); 1170 safe = AccessController.doPrivileged(action); 1171 } catch (Exception e) { // e.g. SecurityException 1172 /* We don't know, so we assume it isn't. */ 1173 safe = false; 1174 } 1175 mbeanInfoSafeMap.put(subclass, safe); 1176 } 1177 return safe; 1178 } 1179 } 1180 1181 static boolean overrides(Class<?> subclass, Class<?> superclass, 1182 String name, Class<?>... params) { 1183 for (Class<?> c = subclass; c != superclass; c = c.getSuperclass()) { 1184 try { 1185 c.getDeclaredMethod(name, params); 1186 return true; 1187 } catch (NoSuchMethodException e) { 1188 // OK: this class doesn't override it 1189 } 1190 } 1191 return false; 1192 } 1193 1194 private static class MBeanInfoSafeAction 1195 implements PrivilegedAction<Boolean> { 1196 1197 private final Class<?> subclass; 1198 1199 MBeanInfoSafeAction(Class<?> subclass) { 1200 this.subclass = subclass; 1201 } 1202 1203 public Boolean run() { 1204 // Check for "void cacheMBeanInfo(MBeanInfo)" method. 1205 // 1206 if (overrides(subclass, StandardMBean.class, 1207 "cacheMBeanInfo", MBeanInfo.class)) 1208 return false; 1209 1210 // Check for "MBeanInfo getCachedMBeanInfo()" method. 1211 // 1212 if (overrides(subclass, StandardMBean.class, 1213 "getCachedMBeanInfo", (Class<?>[]) null)) 1214 return false; 1215 1216 // Check for "MBeanInfo getMBeanInfo()" method. 1217 // 1218 if (overrides(subclass, StandardMBean.class, 1219 "getMBeanInfo", (Class<?>[]) null)) 1220 return false; 1221 1222 // Check for "MBeanNotificationInfo[] getNotificationInfo()" 1223 // method. 1224 // 1225 // This method is taken into account for the MBeanInfo 1226 // immutability checks if and only if the given subclass is 1227 // StandardEmitterMBean itself or can be assigned to 1228 // StandardEmitterMBean. 1229 // 1230 if (StandardEmitterMBean.class.isAssignableFrom(subclass)) 1231 if (overrides(subclass, StandardEmitterMBean.class, 1232 "getNotificationInfo", (Class<?>[]) null)) 1233 return false; 1234 return true; 1235 } 1236 } 1237 }